#include "ParameterReader.hpp"
#include <string.h>
#include <ctype.h>

std::vector<std::string> tokenize(const char* str, const char* delim)
{ 
    std::vector<std::string> tokens;
    char* token = strtok (const_cast<char*>(str), const_cast<char*>(delim));
    while (token != NULL) {
        tokens.push_back(token);
        token = strtok (NULL, delim);
    }
    return tokens;
}

void ParameterReader::SetParameter(const std::string &key, const std::string &in)
{
    params_.insert(std::pair<std::string,std::string>(key,in));
}


//It assume that the value parsed is the right of type!!!!!
bool ParameterReader::readParameters(const std::string &filename)
{
    std::ifstream infile (filename.c_str(), std::ios::in);
    const int max_chars_per_line = 512;
    char *buffer(new char[max_chars_per_line]);
    std::vector<std::string> tokens;
    int n_tokens;
    int counter=1;
    bool reading=true, empty=true;
    std::string error_message("");

    if(!(infile.is_open() && infile.good()))
    {
        std::cerr << std::endl;
        std::cerr << "ERROR: Failed to open file: " << filename << std::endl;
        std::cerr << std::endl;
        reading=false;
        exit(1);
    }
    while(infile.good())
    {
        infile.getline(buffer, max_chars_per_line);
        tokens = tokenize(buffer, " \t\r\n");
        n_tokens=tokens.size();


        //allow for empty lines
        if (n_tokens == 0) {
            ++counter;
            continue;
        } // and allow for comments
        else if (tokens[0][0] == '#') {
            ++counter;
            continue;
        }
        // check for parameter with no value
        if(n_tokens==1)
        {
            std::cerr<<"Error: parameter "<<tokens[0]<<" Does not has a specified value. Check line "<<counter<<" of your parameters file.";
            reading=false;
            exit(1);
        }
        else if(n_tokens>=2)
        {
            empty=false;
            std::string concatenator("");
            std::string paramName = tokens[0];
            for(int j=1;j<n_tokens && tokens[j][0]!='#';j++)
            {
                concatenator += " " + tokens[j];
            }
            const std::string value = tokens[1];
            if (!IsDefined(value))
            {
                SetParameter(paramName,value);
            }

        }
        else
        {
            std::cerr<< "Parameter <" << tokens[0] << "> is specified multiple times. Check the input file line: "<<std::endl;
            delete [] buffer;
            exit(1);
        }
        ++counter;
    }
    if(empty)
        std::cerr<<"The input file is empty. Program executed with default parameters.\n"<<std::endl;
    delete [] buffer;
    infile.close();

    return reading;
}

bool ParameterReader::readParticleFile(real*& masses,real4*& positions,real4*& velocities, const std::string& filename, uint& N_particles)
{
    std::ifstream infile (filename.c_str(), std::ios::in);
    const int max_chars_per_line = 512;
    char *buffer(new char[max_chars_per_line]);
    std::vector<std::string> tokens;
    uint n_tokens,counter=1;
    bool empty=true;
    if(!(infile.is_open() && infile.good()))
    {
        std::cerr << std::endl;
        std::cerr << "ERROR: Failed to open file: " << filename << std::endl;
        std::cerr << std::endl;
        return false;
    }
    uint numLines = 0;
    while ( infile.good() )
    {
       std::string line;
       std::getline(infile, line);
       if(line.length()!=0 && line.at(0)!='#')
        ++numLines;
    }
    if(numLines!=0)
    empty=false;

    N_particles=numLines;
    positions = new real4[N_particles];
    velocities = new real4[N_particles];
    masses = new real[N_particles];

    //std::cout<<"\n"<<numLines<<"\n";
    infile.clear();
    infile.seekg(0, std::ios::beg);
    for(int i=0;i<static_cast<int>(N_particles) && !empty;++i)
    {
        infile.getline(buffer, max_chars_per_line);
        tokens = tokenize(buffer, " \t\r\n");
        n_tokens=tokens.size();
    ++counter;
        if (n_tokens == 0)
    {
        --i;
            continue;
    }
    else if (tokens[0][0] == '#') {
        --i;
        continue;
    }
    else if (n_tokens < 7)
    {
    /*    delete [] masses;
            delete [] velocities;
            delete [] positions;
            delete [] forces;
            delete [] oldForces;
        delete [] particle_ids;
        delete [] inverse2Masses;
    */
        std::cerr << std::endl;
        std::cerr <<"ERROR: Bad specification of particle at line "
              << counter <<" of file "<<filename<< std::endl;
        return false;
    }
    else
    {
        //mass
        masses[i]=atof(tokens[0].c_str());
        //positions
        positions[i](0)=atof(tokens[1].c_str());
        positions[i](1)=atof(tokens[2].c_str());
        positions[i](2)=atof(tokens[3].c_str());
        positions[i](3)=0.0;
        //velocities
        velocities[i](0)=atof(tokens[4].c_str());
        velocities[i](1)=atof(tokens[5].c_str());
        velocities[i](2)=atof(tokens[6].c_str());
        velocities[i](3)=0.0;
    }

    }
    delete [] buffer;
    infile.close();
    if(empty)
      std::cout<<"\nWARNING: The particle file is empty. No particles allocated.\n";
    return true;
}
